package net.xisberto.phonetodesktop;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;

import com.dropbox.client2.exception.DropboxException;
import com.dropbox.client2.exception.DropboxServerException;
import com.dropbox.client2.exception.DropboxUnlinkedException;

public class SendLinkService extends IntentService {
	private final int 
		NOTIFICATOIN_UPLOADING = 2;
	private DropboxSynced dropbox;
	private NotificationManager notif_manager;

	public SendLinkService() {
		super("PhoneToDesktop");
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		updateFile(DropboxSynced.FILENAME, intent.getExtras().getString(Intent.EXTRA_TEXT));
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		dropbox = new DropboxSynced(getApplicationContext());
		return super.onStartCommand(intent, flags, startId);
	}

	private void updateFile(String filename, String text) {
		notificateProgress();
		ByteArrayOutputStream file = new ByteArrayOutputStream();
		try {
			try {
				file = dropbox.download(filename);
			} catch (DropboxServerException server_e) {
				if (server_e.error == DropboxServerException._404_NOT_FOUND) {
					//File doesn't exists in server. We will create a "empty" one.
					file = newJsonFile(filename);
				}
			}
			append(file, extractURLs(text));
			dropbox.upload(file, filename);
			notif_manager.cancel(NOTIFICATOIN_UPLOADING);
		} catch (DropboxUnlinkedException unlinked_e) {
			notif_manager.cancel(NOTIFICATOIN_UPLOADING);
			//User hasn't authorized application, notify the user about it
			dropbox.removeKeys();
			CharSequence tickerText = getResources().getString(R.string.txt_error_sending);
			notificate(android.R.drawable.stat_sys_warning, tickerText, PhoneToDesktopActivity.class);
		} catch (DropboxException dropbox_e) {
			notif_manager.cancel(NOTIFICATOIN_UPLOADING);
			CharSequence tickerText = getResources().getString(R.string.txt_dropbox_problem);
			notificate(android.R.drawable.stat_sys_warning, tickerText, PhoneToDesktopActivity.class);
		}
	}
	
	/**
	 * Extracts any URLs contained in the given text.
	 * @param text the text from which the URLs will be extracted
	 * @return an array of String containing the extracted URLs
	 */
	private String[] extractURLs(String text) {
		//Separate text in spaces; URLs don't have spaces
		String[] parts = text.split("\\s");
		String[] result = new String[parts.length];
		int index = 0;
		for (String part : parts) {
			try {
				URL url = new URL(part);
				result[index] = url.toString();
				index++;
			} catch (StringIndexOutOfBoundsException siob_e) {
				//Some emoticons (eg. :D) cause this exception. Only URLs matter to us.
				continue;
			} catch (MalformedURLException e) {
				//If it isn't an URL, do nothing
				continue;
			}
		}
		if (index == 0) {
			//Nothing was added to result. We could offer a way to send the entire text to Dropbox.
		}
		return result;
	}
	
	/**
	 * Appends each link in strings to the outputStream in json formatting. 
	 * @param outputStream the ByteArrayOutputStream which will be appended
	 * @param strings an array of Strings to append
	 */
	private void append(ByteArrayOutputStream outputStream, String[] strings) {
		for (String string : strings) {
			//string may be null or empty, we skip those
			if ((string == null) || (string.length() == 0)) {
				continue;
			}
			//Creates new link as a JSON object
			JSONObject link = new JSONObject();
			try {
				link.put("url", string);
				link.put("visited", false);
			} catch (JSONException e1) {
				notif_manager.cancel(NOTIFICATOIN_UPLOADING);
				e1.printStackTrace();
			}
			try {
				//Parses the outputStream as a JSONObject
				JSONObject outputJson = new JSONObject(outputStream.toString());
				//Adds the link object created to the outputJson
				JSONArray link_array = outputJson.getJSONArray("links_list");
				link_array.put(link);
				//Writes back the full ouputJson object to outputStream
				byte[] buffer = new byte[0];
				buffer = outputJson.toString(4).getBytes();
				outputStream.reset();
				outputStream.write(buffer);
				outputStream.flush();
			} catch (JSONException e1) {
				notif_manager.cancel(NOTIFICATOIN_UPLOADING);
				e1.printStackTrace();
			} catch (IOException e) {
				notif_manager.cancel(NOTIFICATOIN_UPLOADING);
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Creates a new file in Dropbox cloud. It is a basic json file we will use to populate with new links.
	 * The json file structure is:<br>
	 * <pre>
	 * {
	 *   "links_list": [
	 *     {"url": "http://example.com", "visited": false},
	 *     {"url": "http://example.com/path", "visited": true}
	 *   ]
	 * }
	 * </pre>
	 * @param filename
	 */
	private ByteArrayOutputStream newJsonFile(String filename) {
		ByteArrayOutputStream file = new ByteArrayOutputStream();
		JSONObject root = new JSONObject();
		JSONArray link_array = new JSONArray();
		try {
			root.put("links_list", link_array);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		try {
			file.write(root.toString(4).getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JSONException e) {
			e.printStackTrace();
		}
		try {
			dropbox.upload(file, filename);
		} catch (DropboxUnlinkedException e) {
			e.printStackTrace();
		} catch (DropboxException e) {
			e.printStackTrace();
		}
		return file;
	}
	
	private void notificateProgress() {
		//Configure intent with no activity class
		Intent intent = new Intent();
		PendingIntent pengingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
		
		//Configure the notification
		CharSequence tickerText = getResources().getString(R.string.txt_sending);
		Notification notification = new Notification(android.R.drawable.stat_sys_upload, tickerText, System.currentTimeMillis());
		notification.flags |= Notification.FLAG_ONGOING_EVENT;
		notification.contentIntent = pengingIntent;
		notification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.notification);
		notification.contentView.setTextViewText(R.id.status_text, tickerText);
		notification.contentView.setProgressBar(R.id.status_progress, 100, 0, true);

		notif_manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		notif_manager.notify(NOTIFICATOIN_UPLOADING, notification);
	}
	
	private void notificate(int icon, CharSequence tickerText, Class activity) {
		
		long when = System.currentTimeMillis();
		Notification notification = new Notification(icon, tickerText, when);
		notification.flags|= Notification.FLAG_AUTO_CANCEL;
		
		CharSequence contentTitle = getResources().getString(R.string.app_name);
		CharSequence contentText = getResources().getString(R.string.txt_authorize_dropbox);
		Intent notification_intent = new Intent(this, activity);
		PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notification_intent, 0);
		
		notification.setLatestEventInfo(getApplicationContext(), contentTitle, contentText, contentIntent);

		notif_manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		notif_manager.notify(1, notification);
	}
}
